"""
Enemy tank AI implementation
"""
import pygame
import random
import math
from typing import List, Tuple, Optional
from .tank import Tank
from utils.constants import Direction, TankType
from utils.config import ENEMY_COUNT


class EnemyTank(Tank):
    """Enemy tank with AI behavior"""
    
    def __init__(self, x: int, y: int, tank_type: str = TankType.ENEMY_BASIC, difficulty: str = "normal"):
        """
        Initialize enemy tank
        
        Args:
            x: X coordinate
            y: Y coordinate
            tank_type: Type of enemy tank
            difficulty: AI difficulty level ("easy", "normal", "hard")
        """
        super().__init__(x, y, tank_type)
        
        # AI properties
        self.ai_state = "patrol"  # patrol, chase, attack, retreat
        self.target = None
        self.last_target_update = 0
        self.target_update_interval = 1000  # milliseconds
        self.difficulty = difficulty
        
        # Movement AI
        self.move_timer = 0
        self.move_duration = 0
        self.current_move_direction = Direction.UP
        self.last_position = (x, y)
        self.stuck_timer = 0
        
        # Shooting AI
        self.last_shot_time = 0
        self.shot_cooldown = self._get_shot_cooldown()
        self.aim_accuracy = self._get_aim_accuracy()
        
        # Tank-specific properties
        self._set_tank_properties()
        self._apply_difficulty_modifiers()
    
    def _set_tank_properties(self):
        """Set properties based on tank type"""
        if self.tank_type == TankType.ENEMY_FAST:
            self.speed = 3
            self.shot_cooldown = 800
            self.aim_accuracy = 0.7
        elif self.tank_type == TankType.ENEMY_HEAVY:
            self.speed = 1
            self.shot_cooldown = 2000
            self.aim_accuracy = 0.9
        else:  # ENEMY_BASIC
            self.speed = 2
            self.shot_cooldown = 1200
            self.aim_accuracy = 0.8
    
    def _get_shot_cooldown(self) -> int:
        """Get shot cooldown based on tank type"""
        cooldown_map = {
            TankType.ENEMY_BASIC: 1200,
            TankType.ENEMY_FAST: 800,
            TankType.ENEMY_HEAVY: 2000
        }
        return cooldown_map.get(self.tank_type, 1200)
    
    def _get_aim_accuracy(self) -> float:
        """Get aim accuracy based on tank type"""
        accuracy_map = {
            TankType.ENEMY_BASIC: 0.8,
            TankType.ENEMY_FAST: 0.7,
            TankType.ENEMY_HEAVY: 0.9
        }
        return accuracy_map.get(self.tank_type, 0.8)
    
    def _apply_difficulty_modifiers(self):
        """Apply difficulty-based modifiers to AI behavior"""
        if self.difficulty == "easy":
            # Easier AI - slower reactions, less accurate
            self.target_update_interval *= 1.5
            self.shot_cooldown *= 1.3
            self.aim_accuracy *= 0.7
            self.speed *= 0.8
        elif self.difficulty == "hard":
            # Harder AI - faster reactions, more accurate
            self.target_update_interval *= 0.7
            self.shot_cooldown *= 0.8
            self.aim_accuracy *= 1.2
            self.speed *= 1.1
        # "normal" difficulty uses base values
    
    def set_difficulty(self, difficulty: str):
        """Change AI difficulty level"""
        self.difficulty = difficulty
        self._apply_difficulty_modifiers()
    
    def update_ai(self, dt: float, player_tank: Tank, 
                  other_tanks: List[Tank], game_map) -> bool:
        """
        Update AI behavior
        
        Args:
            dt: Delta time in seconds
            player_tank: Player tank
            other_tanks: Other tanks in game
            game_map: Game map
            
        Returns:
            True if tank should shoot
        """
        current_time = pygame.time.get_ticks()
        
        # Update target
        self._update_target(player_tank, other_tanks, current_time)
        
        # Update AI state
        self._update_ai_state(player_tank, game_map)
        
        # Execute AI behavior
        should_shoot = self._execute_ai_behavior(dt, player_tank, game_map, current_time)
        
        return should_shoot
    
    def _update_target(self, player_tank: Tank, other_tanks: List[Tank], current_time: int):
        """Update AI target"""
        if current_time - self.last_target_update < self.target_update_interval:
            return
        
        self.last_target_update = current_time
        
        # Find closest enemy tank
        closest_distance = float('inf')
        closest_tank = None
        
        # Check player tank
        if player_tank.is_alive():
            distance = self._calculate_distance(player_tank)
            if distance < closest_distance:
                closest_distance = distance
                closest_tank = player_tank
        
        # Check other tanks (for team-based gameplay)
        for tank in other_tanks:
            if tank.is_alive() and tank != self:
                distance = self._calculate_distance(tank)
                if distance < closest_distance:
                    closest_distance = distance
                    closest_tank = tank
        
        self.target = closest_tank
    
    def _update_ai_state(self, player_tank: Tank, game_map):
        """Update AI state based on current situation"""
        if not self.target or not self.target.is_alive():
            self.ai_state = "patrol"
            return
        
        distance = self._calculate_distance(self.target)
        
        # State transitions
        if distance < 150:  # Close range
            self.ai_state = "attack"
        elif distance < 300:  # Medium range
            self.ai_state = "chase"
        else:  # Long range
            self.ai_state = "patrol"
    
    def _execute_ai_behavior(self, dt: float, player_tank: Tank, 
                           game_map, current_time: int) -> bool:
        """Execute AI behavior based on current state"""
        should_shoot = False
        
        if self.ai_state == "patrol":
            self._patrol_behavior(dt, game_map)
        elif self.ai_state == "chase":
            self._chase_behavior(dt, game_map)
        elif self.ai_state == "attack":
            should_shoot = self._attack_behavior(dt, game_map, current_time)
        
        return should_shoot
    
    def _patrol_behavior(self, dt: float, game_map):
        """Patrol behavior - random movement"""
        self.move_timer += dt * 1000
        
        # Change direction periodically
        if self.move_timer >= self.move_duration:
            self.move_timer = 0
            self.move_duration = random.randint(1000, 3000)
            self.current_move_direction = random.choice([
                Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT
            ])
        
        # Move in current direction
        self.move(self.current_move_direction)
        
        # Check if stuck
        self._check_stuck()
    
    def _chase_behavior(self, dt: float, game_map):
        """Chase behavior - move towards target with pathfinding"""
        if not self.target:
            return
        
        # Calculate direction to target
        target_direction = self._get_direction_to_target()
        
        # Try to move towards target
        original_x, original_y = self.x, self.y
        self.move(target_direction)
        
        # Check if movement caused collision
        if self._check_collision_with_map(game_map):
            # Try alternative directions
            self.set_position(original_x, original_y)
            self._try_alternative_movement(game_map)
        
        # Check if stuck
        self._check_stuck()
    
    def _check_collision_with_map(self, game_map) -> bool:
        """Check if tank is colliding with map"""
        tank_rect = self.get_collision_rect()
        return game_map.check_collision(tank_rect)
    
    def _try_alternative_movement(self, game_map):
        """Try alternative movement directions when blocked"""
        directions = [Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT]
        
        # Remove current direction
        if self.direction in directions:
            directions.remove(self.direction)
        
        # Try each direction
        for direction in directions:
            original_x, original_y = self.x, self.y
            self.move(direction)
            
            if not self._check_collision_with_map(game_map):
                return  # Found valid direction
            
            # Restore position
            self.set_position(original_x, original_y)
        
        # If all directions fail, try random movement
        self._random_movement()
    
    def _random_movement(self):
        """Perform random movement when stuck"""
        self.current_move_direction = random.choice([
            Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT
        ])
        self.move(self.current_move_direction)
    
    def _attack_behavior(self, dt: float, game_map, current_time: int) -> bool:
        """Attack behavior - shoot at target with prediction"""
        if not self.target:
            return False
        
        # Face target
        target_direction = self._get_direction_to_target()
        self.rotate(target_direction)
        
        # Check if can shoot
        if current_time - self.last_shot_time >= self.shot_cooldown:
            # Check if target is in line of sight
            if self._can_see_target(game_map):
                # Add prediction for moving targets
                if self._should_predict_shot():
                    self._aim_with_prediction()
                
                self.last_shot_time = current_time
                return True
        
        return False
    
    def _should_predict_shot(self) -> bool:
        """Determine if shot should be predicted based on tank type"""
        prediction_tanks = [TankType.ENEMY_HEAVY]  # Only heavy tanks predict
        return self.tank_type in prediction_tanks
    
    def _aim_with_prediction(self):
        """Aim with prediction for moving targets"""
        if not self.target:
            return
        
        # Calculate target velocity (simplified)
        target_velocity = self._estimate_target_velocity()
        
        # Predict target position
        predicted_x = self.target.x + target_velocity[0] * 0.5  # 0.5 second prediction
        predicted_y = self.target.y + target_velocity[1] * 0.5
        
        # Calculate direction to predicted position
        dx = predicted_x - self.x
        dy = predicted_y - self.y
        
        # Determine primary direction
        if abs(dx) > abs(dy):
            self.direction = Direction.RIGHT if dx > 0 else Direction.LEFT
        else:
            self.direction = Direction.DOWN if dy > 0 else Direction.UP
    
    def _estimate_target_velocity(self) -> Tuple[float, float]:
        """Estimate target velocity for prediction"""
        # This is a simplified velocity estimation
        # In a more complex implementation, you would track target position history
        if not self.target:
            return (0, 0)
        
        # Simple estimation based on target movement
        if hasattr(self.target, 'moving') and self.target.moving:
            dx, dy = self.target._get_direction_vector(self.target.direction)
            return (dx * self.target.speed, dy * self.target.speed)
        
        return (0, 0)
    
    def _get_direction_to_target(self) -> int:
        """Get direction to face target"""
        if not self.target:
            return Direction.UP
        
        dx = self.target.x - self.x
        dy = self.target.y - self.y
        
        # Determine primary direction
        if abs(dx) > abs(dy):
            return Direction.RIGHT if dx > 0 else Direction.LEFT
        else:
            return Direction.DOWN if dy > 0 else Direction.UP
    
    def _calculate_distance(self, target: Tank) -> float:
        """Calculate distance to target"""
        dx = target.x - self.x
        dy = target.y - self.y
        return math.sqrt(dx * dx + dy * dy)
    
    def _can_see_target(self, game_map) -> bool:
        """Check if target is in line of sight using raycasting"""
        if not self.target:
            return False
        
        # Calculate distance
        distance = self._calculate_distance(self.target)
        if distance > 300:  # Max sight range
            return False
        
        # Perform raycasting to check line of sight
        return self._raycast_to_target(game_map)
    
    def _raycast_to_target(self, game_map) -> bool:
        """Perform raycasting to check if target is visible"""
        if not self.target:
            return False
        
        # Calculate direction to target
        dx = self.target.x - self.x
        dy = self.target.y - self.y
        distance = math.sqrt(dx * dx + dy * dy)
        
        if distance == 0:
            return True
        
        # Normalize direction
        dx /= distance
        dy /= distance
        
        # Cast ray from tank to target
        step_size = 16  # Check every 16 pixels
        steps = int(distance / step_size)
        
        for i in range(1, steps + 1):
            check_x = self.x + dx * i * step_size
            check_y = self.y + dy * i * step_size
            
            # Check if ray hits a solid tile
            tile = game_map.get_tile_at_position(int(check_x), int(check_y))
            if tile and tile.solid:
                return False
        
        return True
    
    def _check_stuck(self):
        """Check if tank is stuck and try to unstick"""
        current_position = (self.x, self.y)
        
        if current_position == self.last_position:
            self.stuck_timer += 1
            if self.stuck_timer > 60:  # Stuck for 1 second at 60 FPS
                # Try random direction
                self.current_move_direction = random.choice([
                    Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT
                ])
                self.stuck_timer = 0
        else:
            self.stuck_timer = 0
        
        self.last_position = current_position
    
    def render(self, screen: pygame.Surface):
        """Render enemy tank with AI indicators"""
        if not self.is_alive():
            return
        
        # Call parent render method
        super().render(screen)
        
        # Add AI-specific visual indicators
        self._render_ai_indicators(screen)
    
    def _render_ai_indicators(self, screen: pygame.Surface):
        """Render AI-specific visual indicators"""
        # Draw AI state indicator
        font = pygame.font.Font(None, 12)
        state_text = font.render(self.ai_state, True, (255, 255, 255))
        screen.blit(state_text, (self.x, self.y - 15))
        
        # Draw target line if in attack mode
        if self.ai_state == "attack" and self.target and self.target.is_alive():
            pygame.draw.line(screen, (255, 0, 0), 
                           (self.x + self.width // 2, self.y + self.height // 2),
                           (self.target.x + self.target.width // 2, 
                            self.target.y + self.target.height // 2), 2)
        
        # Draw health bar
        if self.lives > 0:
            bar_width = self.width
            bar_height = 4
            bar_x = self.x
            bar_y = self.y - 8
            
            # Background
            pygame.draw.rect(screen, (255, 0, 0), 
                           (bar_x, bar_y, bar_width, bar_height))
            
            # Health
            health_width = int(bar_width * (self.lives / 1))  # Assuming max 1 life
            pygame.draw.rect(screen, (0, 255, 0), 
                           (bar_x, bar_y, health_width, bar_height))